package puf2

import chisel3._
import chisel3.util._
import chisel3.experimental._

import puf2._ 
import puf2.PUF_Constants._

import common._


class puf_top extends Module{
    val io = IO( new Bundle{
        val out =   new PUF_Top_Output_Ports
        val ctr =   new PUF_Control_Ports
    } )

    val M_PUF_SubTop    =   Module(new puf_subtop)
    val M_Clk_Divider   =   Module(new clk_divider)

    val the_clock       =   Wire(Clock())
    M_Clk_Divider.io.clk    :=  clock
    M_Clk_Divider.io.rst_p  :=  reset.asBool
    the_clock               :=  M_Clk_Divider.io.clk_div 

    M_PUF_SubTop.clock      :=  the_clock
    io.out                  <>  M_PUF_SubTop.io.out 
    io.ctr                  <>  M_PUF_SubTop.io.ctr 

}

class puf_subtop extends Module{
    val io  =   IO(new Bundle{
        val out =   new PUF_Top_Output_Ports
        val ctr =   new PUF_Control_Ports
    })

    val M_PUF_Core      =   Module(new puf_core)
    val M_read_data     =   Module(new read_data)
    val the_clock       =   Clk_Divider2()

    val the_clock_reverse   =   Wire(Clock())
    the_clock_reverse       :=  Mux(the_clock.asBool, false.B, true.B).asClock

    //  M_PUF_Core.io.clk.Clk   :=  the_clock
    M_PUF_Core.io.clk.Clk           :=  the_clock_reverse
    // M_PUF_Core.io.clk.Clk_reverse   :=  the_clock

    M_read_data.clock       :=  the_clock
    M_read_data.io.ena      :=  RegNext(M_PUF_Core.io.out.Ena_out) 
    M_read_data.io.channel  :=  io.out.Data_out

    val the_ena_in          =   RegNext(M_PUF_Core.io.out.Ena_in)
    val the_wl_enable       =   Mux( io.ctr.puf_wl_ena,     the_ena_in, false.B )
    val the_write_ena       =   Mux( io.ctr.puf_write_ena,  the_ena_in, false.B )

    io.out.Data_in          :=  RegNext(M_PUF_Core.io.out.Data_in)
    io.out.Ena_in           :=  the_ena_in
    io.out.Ena_out          :=  RegNext(M_PUF_Core.io.out.Ena_out)
    io.out.Clk_in           :=  RegNext(M_PUF_Core.io.out.Clk_in && the_clock.asUInt.asBool)
    io.out.Clk_out          :=  RegNext(M_PUF_Core.io.out.Clk_out && the_clock_reverse.asUInt.asBool)
    // io.out.Clk_out          :=  RegNext(M_PUF_Core.io.out.Clk_out && the_clock.asUInt.asBool)

    io.out.Clk              :=  RegNext(the_ena_in)
    io.out.WL_Enable        :=  RegNext(the_wl_enable)
    io.out.Write_Ena        :=  RegNext(the_write_ena)

    M_PUF_Core.io.ctr.puf_in_valid  :=  io.ctr.puf_in_valid
    io.ctr.puf_in_ready             :=  M_PUF_Core.io.ctr.puf_in_ready
    M_PUF_Core.io.ctr.puf_data_in   :=  io.ctr.puf_data_in

    io.ctr.puf_out_valid            :=  M_PUF_Core.io.ctr.puf_out_valid
    M_PUF_Core.io.ctr.puf_out_ready :=  io.ctr.puf_out_ready
    io.ctr.puf_data_out             :=  M_read_data.io.output

    M_PUF_Core.io.ctr.puf_wl_ena    :=  io.ctr.puf_wl_ena
    M_PUF_Core.io.ctr.puf_write_ena :=  io.ctr.puf_write_ena

}

class read_data extends Module{
    val io = IO(new Bundle{
        val ena     =   Input(Bool())
        // val count   =   Input(UInt(WIDTH_CNT.W))
        val channel =   Input(UInt(1.W))
        val output  =   Output(UInt(TRANS_CNT.W))
    })

    val result      =   RegInit(0.U(TRANS_CNT.W))
    val count       =   RegInit(0.U(WIDTH_CNT.W))

    when( io.ena ){
        count           :=  count + 1.U
    }.otherwise{
        count           :=  0.U
    }
    

    when( io.ena && ( count < TRANS_CNT.U -1.U ) ){ // && count <= TRANS_CNT
        // result  :=  Cat( result(TRANS_CNT-2, 0), io.channel )   // !!! left shift
        result  :=  Cat( io.channel, result(TRANS_CNT-1, 1) )   // !!! right shift
    }

    io.output       :=  result
    
    // when( (count === TRANS_CNT.U) && ~io.ena ){
    //     io.output       :=  result
    // }.otherwise{
    //     io.output       :=  0.U 
    // }

}